<template>
  <div :id="containerId" class="upload-plus" :class="uploadClass">
    <ul
      v-if="listType === 'picture-card' || listType === 'picture'"
      class="el-upload-list el-upload-list--picture-card"
    >
      <li
        v-for="(item, i) in fileList"
        :key="item.id"
        :tabindex="i"
        class="el-upload-list__item "
        :style="{ width: width * scale + 'px', height: height * scale + 'px' }"
        :class="item.waitUpload ? 'is-ready' : 'is-success'"
        :data-id="item.id"
      >
        <video
          v-if="dir === 'video'"
          :width="width + 'px'"
          :height="height + 'px'"
          :src="item.url"
        >
          当前浏览器不支持 video直接播放
        </video>
        <div
          v-else
          class="picture-show-contatiner el-upload-list__item-thumbnail"
          :style="{ 'background-image': 'url(' + item.url + ')' }"
        />
        <a class="el-upload-list__item-name">
          <i class="el-icon-document" />{{ item.name }}
        </a>
        <label class="el-upload-list__item-status-label">
          <i
            class="el-icon-upload-success el-icon-check"
            :style="{ transform: 'rotate(-45deg) scale(' + scale * 1.5 + ')' }"
          />
        </label>
        <span class="el-upload-list__item-actions">
          <el-tooltip content="点击查看图片">
            <span class="el-upload-list__item-preview">
              <i
                class="el-icon-zoom-in"
                @click="handlePictureCardPreview(item)"
              />
            </span>
          </el-tooltip>
          <el-tooltip v-if="!readonly" content="点击删除图片">
            <span class="el-upload-list__item-delete">
              <popover-delete @confirm="removeFile(item)">
                <i class="el-icon-delete" />
              </popover-delete>
            </span>
          </el-tooltip>
          <el-tooltip v-if="showExtBtn && !readonly" :content="extBtnTip">
            <span class="el-upload-list__item-preview">
              <i class="el-icon-setting" @click="onExtBtn(item)" />
            </span>
          </el-tooltip>
        </span>
        <el-progress
          v-if="item.waitUpload && item.percent"
          type="circle"
          :stroke-width="strokeWidth"
          :percentage="item.percent"
          :status="item.percent === 100 ? 'success' : 'text'"
        />
        <slot name="otherSlot" :data="{ item, index: i }" />
      </li>
    </ul>

    <div v-show="!readonly" style="flex: none;">
      <!-- text 样式 自动上传-->
      <el-button
        v-if="listType == 'text' && autoUpload"
        :id="selectFileId"
        :size="btnSize"
        icon="el-icon-upload"
        type="primary"
      >点击上传
      </el-button>
      <!-- text 样式 且 不自动上传 -->
      <div v-else-if="listType == 'text' && !autoUpload">
        <el-button
          :id="selectFileId"
          :size="btnSize"
          type="primary"
          icon="el-icon-document"
        >选取文件</el-button>
        <el-button
          v-show="fileList.length > 0"
          :size="btnSize"
          type="success"
          class="ml8"
          icon="el-icon-upload"
          @click="startUpload"
        >上传到服务器</el-button>
      </div>
      <!-- picture 'picture-card' 样式 自动上传-->
      <div
        v-show="showPictureSelectBtn"
        :id="selectFileId"
        class="el-upload el-upload--picture-card mb8"
        :style="{ width: width * scale + 'px', height: height * scale + 'px' }"
      >
        <slot name="picture-wrap">
          <i class="el-icon-plus" />
        </slot>
      </div>
    </div>
    <slot name="tip" />

    <!--如果显示文件列表 且 是 text 方式显示-->
    <ul
      v-if="showFileList && listType === 'text'"
      class="el-upload-list el-upload-list--text"
    >
      <li
        v-for="(item, i) in fileList"
        :key="item.id ? item.id : item.localId"
        tabindex="0"
        class="el-upload-list__item "
        :class="item.waitUpload ? 'is-ready' : 'is-success'"
        :style="readonly && i === 0 ? 'margin-top:0px' : ''"
      >
        <slot name="textLabelSlot" :data="{ item, index: i }" />
        <a class="el-upload-list__item-name" @click="clickFileHandler(item)">
          <i class="el-icon-document" />{{ item.name }}</a>
        <label
          class="el-upload-list__item-status-label"
          :class="{ 'block-im': readonly }"
        >
          <i class="el-icon-upload-success el-icon-circle-check" />
        </label>
        <el-tooltip v-if="!readonly" content="点击删除文件">
          <popover-delete class="fr mr24" @confirm="removeFile(item)">
            <i class="el-icon-close" />
          </popover-delete>
        </el-tooltip>
        <el-progress
          v-if="item.waitUpload"
          :stroke-width="strokeWidth"
          :percentage="item.percent"
          :status="item.percent === 100 ? 'success' : 'text'"
        />
      </li>
    </ul>

    <el-dialog
      top="20px"
      :visible.sync="dialogVisible"
      custom-class="upload-image-dialog"
      size="tiny"
      style="z-index: 100000"
      :append-to-body="true"
    >
      <video
        v-if="dir === 'video'"
        style="z-index: 1;"
        width="100%"
        src="movie.ogg"
        controls="controls"
        :src="dialogImagePath"
      >
        您的浏览器不支持 video 标签。
      </video>
      <img v-else width="100%" :src="dialogImagePath" alt="">
    </el-dialog>
  </div>
</template>
<script>
import plupload from 'plupload'
import popoverDelete from '@/components/Delete/index.vue'
import { Upload } from '@/api/upload'

function noop() {}

export default {
  name: 'PloadPlus',
  components: {
    popoverDelete
  },
  props: {
    uploadClass: String, // 上传组件的样式名称
    name: {
      type: String,
      default: 'file'
    }, // 未知
    dir: '', // 上传的文件夹名称    images=>商品、款式图片 material=>素材 video=>视频  washing=>洗涤图标
    drag: Boolean, // 没有地方用到
    showFileList: {
      type: Boolean,
      default: true
    }, // 是否显示已上传的文件夹列表
    accept: String, // 没有地方用到
    type: {
      type: String,
      default: 'select'
    }, // 没有地方用到
    onSuccess: {
      type: Function,
      default: noop
    }, // 成功的方法
    beforeRemove: {
      type: Function,
      default: noop
    }, // 删除之前调用的方法
    afterRemove: {
      type: Function,
      default: noop
    }, // 删除之后调用的方法
    onError: {
      type: Function,
      default: noop
    }, // 报错的方法
    beforeUpload: {
      type: Function,
      default: noop
    }, // 上传前的方法
    finishUpload: {
      type: Function,
      default: noop
    }, // 上传完成的方法
    files: {
      type: [Array, String],
      default() {
        return []
      }
    }, // 文件列表
    autoUpload: {
      type: Boolean,
      default: true
    }, // 是否自动上传
    listType: {
      type: String,
      default: 'text' // text,picture,picture-card
    }, // 展示的文件类型  text文本列表模式
    readonly: {
      type: Boolean,
      default: false
    }, // 只读模式
    btnSize: {
      type: String,
      default: 'small' // medium, small, mini
    }, // btn按钮的尺寸
    disabled: Boolean, // 没有地方用到
    limit: {
      type: Number,
      default: -1
    }, // 限制上传个数，-1为不限制
    max_file_size: {
      type: String,
      default: '500mb'
    },
    removeType: {
      type: String,
      default: 'server'
    }, // 删除模式（服务器删除：server , 仅客户端页面移除 client）
    multiple: {
      type: Boolean,
      default: true
    }, // 打开window文件选择时窗口时是否可以多选文件
    showExtBtn: {
      type: Boolean,
      default: false
    }, // 显示自定义按钮
    extBtnTip: String, // 调用自定义按钮方式前的提示信息
    onExtBtn: {
      type: Function,
      default: noop
    }, // 自定义按钮的方法
    onClickFile: {
      type: Function,
      default: noop
    }, // 当模式为 “text”时，点击文件列表回调事件
    uploadType: {
      type: Array,
      default() {
        return ['txt']
      }
    }, // 上传类型  图片:img ||  Flash:flash|| 文档:txt || 视频:video || 压缩文件:zip || 音频:map3   可以选择多个
    name: {
      type: String,
      default: 'name'
    }, // 想要显示内容名称对应的键名  例如图片名称
    url: {
      type: String,
      default: 'url'
    }, // 想要显示资源路径对应的键名   例如图片  视频
    id: {
      type: [String, Number],
      default: ''
    }, // 和调用页面约定的标记  有可能存在一个页面多个地方调用的情况
    width: {
      type: [Number, String],
      default: 148
    },
    height: {
      type: [Number, String],
      default: 148
    },
    scale: {
      type: [Number, String],
      default: 0.6
    }
  },

  data() {
    return {
      strokeWidth: 4,
      dialogVisible: false,
      dialogImagePath: '',
      fileMap: {},
      containerId: 'plupload_container_' + this.guid(), // 上传容器id
      selectFileId: 'plupload_selectFile_' + this.guid(), // 选择文件按钮id
      uploading: false, // 上传状态：是否上传中  文档
      host: '', // 图片上传后的域名
      signature_param: {
        key: '',
        policy: '',
        OSSAccessKeyId: '',
        success_action_status: '200', // 让服务端返回200,不然，默认会返回204
        callback: '',
        signature: ''
      },
      fileList: [],
      fileType: '' // 判断传入文件类型  后续返回给外面也要相同类型  stringArray  objectArray  string
    }
  },
  computed: {
    showPictureSelectBtn: function() {
      if (this.listType === 'picture-card') {
        if (this.fileType === 'string' && this.fileList.length > 0) { return false }
        return true
      } else if (this.listType === 'picture') {
        if (this.fileList.length > 0) return false
        return true
      } else {
        // text类型不显示
        return false
      }
    }
  },
  watch: {
    fileList(newValue, oldValue) {
      this.refresh()
    },
    readonly: {
      handler(newValue) {
        if (!newValue) this.getSignature()
      },
      immediate: true
    },
    files: {
      handler(newVal) {
        this.initFileList()
      },
      immediate: true
    }
  },
  mounted() {},
  updated() {},
  methods: {
    finishUploadPlus(id, list) {
      let newList = ''
      switch (this.fileType) {
        case 'string':
          newList = list[0].url
          break
        case 'stringArray':
          newList = list.map(item => item.url)
          break
        default:
          // objectArray 不处理
          newList = list
      }

      this.finishUpload(id, newList)
    },
    initFileList() {
      // 进行数据规范整合
      if (typeof this.files === 'object' && this.files != null) {
        this.fileList = this.files.map(item => {
          let obj = {}
          // 如果item 是字符串  则 this.file 的数据结构是['img.png','img2.png']  但是我们需要的结构是  [{name:'...',url:'...'},{name:'...',url:'...'}];
          if (typeof item === 'string') {
            this.fileType = 'stringArray'
            obj.url = item
            obj.name = ''
          }
          if (typeof item === 'object') {
            this.fileType = 'objectArray'
            obj = {
              url: item[this.url],
              name: item[this.name],
              ...item
            }
          }
          return obj
        })
      }

      // 将字符串转换成数组  当this.files 是字符串时  则表示 传进来的只有一张图片或者一个视频
      if (typeof this.files === 'string') {
        this.fileType = 'string'
        this.fileList = [{ name: '', url: this.files }]
      }

      this.fileList = this.fileList.filter(item => {
        return item.url != '' && item.url != null
      })
    },
    // 获取阿里云签名
    getSignature() {
      // depot.post({
      //   url: 'Publics/getAliyunOssSignature',
      //   data: {
      //     dir: this.dir
      //   },
      //   cb: ({ data }) => {
      //     this.host = data.host
      //     this.signature_param = {
      //       key: data.dir,
      //       policy: data.policy,
      //       OSSAccessKeyId: data.accessid,
      //       success_action_status: '200', // 让服务端返回200,不然，默认会返回204
      //       callback: data.callback,
      //       signature: data.signature
      //     }
      //     this.pluploadInit()
      //   },
      //   error: error => {
      //     tips(this, 'error', res.msg)
      //   }
      // })

      Upload.getAliyunOssSignature({ dir: this.dir }).then(({ data }) => {
        this.host = data.host
        this.signature_param = {
          key: data.dir,
          policy: data.policy,
          OSSAccessKeyId: data.accessid,
          success_action_status: '200', // 让服务端返回200,不然，默认会返回204
          callback: data.callback,
          signature: data.signature
        }
        this.pluploadInit()
      }).catch(() => {})
    },
    s4() {
      return ((1 + Math.random()) * 0x10000 || 0).toString(16).substring(1)
    },
    guid() {
      return (
        this.s4() +
        Date.now() +
        Math.floor(Math.random() * 999999)
      ).replace(new RegExp('\\.', 'gm'), '')
    } /* 获取文件的后缀*/,
    getSuffix(fileName) {
      var pos = fileName.lastIndexOf('.')
      var su = ''
      if (pos != -1) {
        su = fileName.substring(pos + 1)
        su = su.toLowerCase()
      }
      return su
    },
    /* 获取文件的名称部分（不包含后缀）*/
    getName(fileName) {
      var pos = fileName.lastIndexOf('.')
      var name = fileName
      if (pos != -1) {
        name = fileName.substr(0, pos)
      }
      return name
    },
    removeFile(item) {
      if (!item || this.beforeRemove(item) === false) return
      // 未上传的文件
      if (item.waitUpload === true || this.removeType === 'client') {
        var file = this.uploader.getFile(item.localId)
        this.uploader.removeFile(file)
        this.afterRemove(item, file)
        this.fileList = this.fileList.filter(i => {
          return i.url !== item.url
        })
        this.finishUploadPlus(this.id, this.fileList)
      } else {
        // depot.post({
        //   url: 'Publics/imageDel',
        //   data: {
        //     imgurl: item.url
        //   },
        //   cb: ({ data }) => {
        //     this.afterRemove(item)
        //     this.fileList = this.fileList.filter(i => {
        //       return i.url !== item.url
        //     })
        //     this.finishUploadPlus(this.id, this.fileList)
        //   },
        //   error: error => {
        //     tips(this, 'error', res.msg)
        //   }
        // })
      }
    },
    pluploadInit(arg) {
      var that = this
      // 图片:img ||  Flash:flash|| 文档:txt || 视频:video || 压缩文件:zip || 音频:map3   可以选择多个
      const filter_list = [
        {
          title: 'flash',
          extensions: 'swf',
          mimeTypes: 'application/*'
        },
        {
          title: 'txt',
          extensions: 'txt,doc,docx,ppt,pptx,xls,xlsx,pdf',
          mimeTypes:
            'text/plain,application/msword,application/powerpoint,application/excel,application/pdf'
        },
        {
          title: 'video',
          extensions: 'flv,FLV,mp4,wmv,mpeg',
          mimeTypes: 'video/mp4,video/wmv,video/mpeg,video/x-flv'
        },
        {
          title: 'img',
          extensions: 'jpg,jpeg,gif,png',
          mimeTypes: 'image/jpg,image/jpeg,image/png,image/gif'
        },
        {
          title: 'zip',
          extensions: 'zip,ZIP',
          mimeTypes: 'multipart/x-zip'
        },
        {
          title: 'map3',
          extensions: 'mp3',
          mimeTypes: 'audio/mpeg'
        }
      ]

      const last_filters = filter_list.filter(item => {
        const ext = that.uploadType.includes(item.title)
        return ext
      })
      that.uploader = new plupload.Uploader({
        runtimes: 'html5,flash,silverlight,html4', // 用来指定上传方式，指定多个上传方式请使用逗号隔开
        browse_button: that.selectFileId, // 触发文件选择对话框的DOM元素，当点击该元素后便后弹出文件选择对话框。该值可以是DOM元素对象本身，也可以是该DOM元素的id
        multi_selection: that.multiple, // 是否可以在文件浏览对话框中选择多个文件
        dragdrop: true,
        unique_names: true,
        container: document.getElementById(that.containerId), // 用来指定Plupload所创建的html结构的父容器，默认为前面指定的browse_button的父元素。该参数的值可以是一个元素的id,也可以是DOM元素本身。
        flash_swf_url: 'lib/plupload-2.1.2/js/Moxie.swf', // flash上传组件的url地址，如果是相对路径，则相对的是调用Plupload的html文档。当使用flash上传方式会用到该参数。
        silverlight_xap_url: 'lib/plupload-2.1.2/js/Moxie.xap', // silverlight上传组件的url地址，如果是相对路径，则相对的是调用Plupload的html文档。当使用silverlight上传方式会用到该参数。
        url: 'http://oss.aliyuncs.com', // 服务器端接收和处理上传文件的脚本地址，可以是相对路径(相对于当前调用Plupload的文档)，也可以是绝对路径
        // required_features: 'send_binary_string',//可以使用该参数来设置你必须需要的一些功能特征，Plupload会根据你的设置来选择合适的上传方式。因为，不同的上传方式，支持的功能是不同的，比如拖拽上传只有html5上传方式支持，图片压缩则只有html5,flash,silverlight上传方式支持。该参数的值是一个混合类型，可以是一个以逗号分隔的字符串
        filters: {
          mime_types: last_filters, // 允许上传哪些类型文件的集合
          max_file_size: that.max_file_size, // 上传文件大小的限制
          prevent_duplicates: true // 不允许选取重复文件
        },
        init: {
          // 当Init事件发生后触发监听函数参数：(uploader) uploader为当前的plupload实例对象
          PostInit: function() {
            if (
              that.listType === 'picture' ||
              that.listType === 'picture-card'
            ) {
              that.autoUpload = true
            }
          },
          // 当文件添加到上传队列后触发
          // 监听函数参数：(uploader,files)
          // uploader为当前的plupload实例对象，files为一个数组，里面的元素为本次添加到上传队列里的文件对象
          FilesAdded: function(up, files) {
            var failFileType = {}
            plupload.each(files, function(file) {
              var name = that.getName(file.name)
              name = name.length > 50 ? name.substr(0, 50) : name
              var suffix = that.getSuffix(file.name)
              if (suffix === '') {
                up.removeFile(file)
                failFileType[suffix] = true
                return
              }
              var newFile = {
                name: name,
                suffix: suffix,
                id: null,
                localId: file.id, // 本地文件ID
                waitUpload: true,
                size: file.size,
                percent: 0,
                key: that.host + that.dir + '/' + file.id + '.' + suffix
              }
              // 处理图片前端显示
              that.showImg(file, newFile)
              that.fileMap[file.id] = newFile
              if (that.limit === 1) {
                that.removeFile(that.fileList.shift())
              }
              that.fileList.push(newFile)
            })
            var msglist = ''
            for (var key in failFileType) {
              msglist += key + '、'
            }
            if (msglist !== '') {
              msglist = msglist.slice(0, msglist.length - 1)
              that.$message.error('不可上传 ' + msglist + ' 类型的文件')
              return
            }
            if (that.autoUpload) {
              that.startUpload()
            }
          },
          // 当队列中的某一个文件正要开始上传前触发
          // 监听函数参数：(uploader,file)
          // uploader为当前的plupload实例对象，file为触发此事件的文件对象
          BeforeUpload: function(up, file) {
            that.signature_param.key = that.dir + '/' + file.target_name
            up.setOption({
              url: that.host,
              multipart_params: that.signature_param
            })
          },
          // 会在文件上传过程中不断触发，可以用此事件来显示上传进度
          // 监听函数参数：(uploader,file)
          // uploader为当前的plupload实例对象，file为触发此事件的文件对象
          UploadProgress: function(up, file) {
            that.fileMap[file.id].percent = file.percent
          },
          // 当队列中的某一个文件上传完成后触发
          // 监听函数参数：(uploader,file,responseObject)
          // uploader为当前的plupload实例对象，file为触发此事件的文件对象，responseObject为服务器返回的信息对象，它有以下3个属性：
          // response：服务器返回的文本
          // responseHeaders：服务器返回的头信息
          // status：服务器返回的http状态码，比如200
          FileUploaded: function(up, file, info) {
            that.uploader.removeFile(file)
            if (info.status == 200) {
              that.fileMap[file.id].waitUpload = false
              var result = JSON.parse(info.response)
              if (result.Status === 'Ok') {
                that.fileList.forEach(item => {
                  if (item.localId === file.id) {
                    item.url = item.key
                  }
                })
                that.$message.success(file.name + '上传成功！')
                that.onSuccess(file)
                if (that.uploader.total.queued === 0) { that.finishUploadPlus(that.id, that.fileList) }
              } else {
                that.$message.error('上传文件失败')
                that.onError(file)
              }
            } else {
              // 上传失败处理
              that.onError(file)
            }
          },
          // 当发生错误时触发
          // 监听函数参数：(uploader,errObject)
          // uploader为当前的plupload实例对象，errObject为错误对象，它至少包含以下3个属性(因为不同类型的错误，属性可能会不同)：
          // code：错误代码，具体请参考plupload上定义的表示错误代码的常量属性
          // file：与该错误相关的文件对象
          // message：错误信息
          Error: function(up, err) {
            var msg = err.message
            if (err.code == -600) {
              msg = '文件大小超过上传限制'
            }
            that.$message.error(msg)
            that.onError(err)
          },
          // 当上传队列中所有文件都上传完成后触发
          // 监听函数参数：(uploader,files)
          // uploader为当前的plupload实例对象，files为一个数组，里面的元素为本次已完成上传的所有文件对象
          UploadComplete: function(uploader, files) {}
        }
      })

      // 当前节点初始化
      that.uploader.init()
    },
    // 开始上传
    startUpload() {
      if (this.uploader.total.queued > 0) {
        this.beforeUpload()
      }
      this.uploader.start()
    },
    // 显示图片
    showImg(file, obj) {
      if (this.listType !== 'picture' && this.listType !== 'picture-card') { return }
      // 读取File对象的数据
      try {
        // IE9 不兼容FileReader
        var reader = new FileReader()
        reader.onload = function(evt) {
          obj.url = evt.target.result
        }
        reader.readAsDataURL(file.getNative())
      } catch (error) {
        console.log('读取File对象的数据报错', error)
      }
    },
    handlePictureCardPreview(item) {
      this.dialogImagePath = item[this.url]
      this.dialogVisible = true
    },
    refresh() {
      // 上传按钮位置有改变时需要刷新上传组件
      this.$nextTick(() => {
        this.uploader && this.uploader.refresh()
      })
    },
    clickFileHandler(item) {
      this.onClickFile(item)
    }
  }
}
</script>

<style lang="scss">
.upload-plus {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  .block-im {
    display: block !important;
  }

  .el-upload-list--picture-card {
    display: inline-flex;
    flex-wrap: wrap;
  }

  .picture-show-contatiner {
    background-size: cover;
    background-position: center;
  }
  .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    .el-icon-plus {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
  }

  .el-upload:hover {
    border-color: #409eff;
    .el-icon-plus {
      color: #409eff;
    }
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    line-height: 178px;
    text-align: center;
  }

  .avatar {
    width: 178px;
    height: 178px;
    display: block;
  }

  .el-upload-list__item-actions .el-popover__reference {
    margin-left: 0 !important;
  }
}

.upload-image-dialog {
  z-index: 100001;

  .el-dialog__header {
    padding: 0px;
  }
  .el-dialog__headerbtn {
    z-index: 100;
  }
  .el-dialog__body {
    padding: 5px 5px 0px 5px;
  }
}
</style>
